/**
 * This file is part of Adaptools.
 * 
 * Adaptools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Adaptools is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 * along with Adaptools.  If not, see http://www.gnu.org/licenses/. 
 */
package org.adaptools.base;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.adaptools.base.utils.AdaptoolsBaseMessages;
import org.apache.log4j.Logger;

/**
 * This class is a queue that the Mechanism will read and operate.
 * 
 * @author Denys G. Santos (gsdenys@gmail.com)
 * @since 3.0.0
 */
public class BasicQueue implements Queue, Serializable {
	Logger log = Logger.getLogger(BasicQueue.class);

	/**
	 * Serial Version UID
	 */
	private static final long serialVersionUID = -2433206030421248659L;

	/**
	 * The position of the pointer in the queue.
	 */
	private Integer pointer = 0;

	/**
	 * Collection to represent the queue
	 */
	private Collection<String> collection = new ArrayList<String>();

	/**
	 * Default Builder to create a new empty {@link BasicQueue}.
	 */
	public BasicQueue() {
		String id = AdaptoolsBaseMessages.MESSAGE_CREATE_EMPTY_QUEUE.getId();

		log.debug("WARNING " + id + ": Create an empty queue.");
	}

	/**
	 * @see org.adaptools.base.BasicQueue#load(String)
	 */
	public void load(String input) {
		// check if the input is empty
		if (input == null) {
			String id = AdaptoolsBaseMessages.WARN_POPULATE_QUEUE_WITH_NULL.getId();
			
			log.warn(id + ": Load a null value.");

			return;
		}

		String[] strArray = input.split("");
		List<String> list = new ArrayList<String>(Arrays.asList(strArray));
		list.remove(0);

		this.collection.addAll(list);

		String id = AdaptoolsBaseMessages.MESSAGE_SIMPLE_QUEUE_LOADED.getId();

		log.info(id + ": The \"" + input + "\" Value was loaded.");
	}

	/**
	 * @see org.adaptools.base.BasicQueue#load(String, String)
	 */
	public void load(String input, String separator) {
		// check if the input is empty
		if (input == null) {
			String id = AdaptoolsBaseMessages.WARN_POPULATE_QUEUE_WITH_NULL.getId();	

			log.warn(id + ": Null value was loaded.");
			return;
		}
		
		separator = (separator != null) ? separator : new String(";");
		String[] str = input.split(separator);
		this.collection = Arrays.asList(str);

		String id = AdaptoolsBaseMessages.MESSAGE_QUEUE_LOADED.getId();
		
		log.info(id + ": The \"" + input + "\" Value was loaded.");
	}

	/**
	 * @see org.adaptools.base.BasicQueue#pull()
	 */
	public String pull() {
		// log case the pointer in stack overflow and return null
		if (this.pointer >= this.collection.size()) {
			String id = AdaptoolsBaseMessages.ERROR_QUEUE_POINTER_STACK_OVERFLOW.getId();
			
			log.info(id + ": Pointer alread in Stack Overflow");
			
			return null;
		}

		String str = new ArrayList<String>(this.collection).get(this.pointer);
		this.pointer++;

		// log case the next element
		if (this.pointer == this.collection.size()) {
			String id = AdaptoolsBaseMessages.MESSAGE_QUEUE_POINTER_SETTLED_STACK_OVERFLOW.getId();
			
			log.info(id + ": The pointer is was settled in stack overflow.");
			
			log.info(id);
		}

		return str;
	}

	/**
	 * @see org.adaptools.base.BasicQueue#read()
	 */
	public String read() {
		// log case the pointer in stack overflow and return null
		if (this.pointer >= this.collection.size()) {
			String id = AdaptoolsBaseMessages.ERROR_QUEUE_POINTER_STACK_OVERFLOW.getId();
			
			log.info(id + ": The pointer is alread in stack overflow");
			
			return null;
		}

		return new ArrayList<String>(this.collection).get(this.pointer);
	}

	/**
	 * @see org.adaptools.base.BasicQueue#readNext()
	 */
	public String readNext() {
		Integer localPointer = this.pointer + 1;

		// log case the pointer in stack overflow and return null
		if (localPointer >= this.collection.size()) {
			String id = AdaptoolsBaseMessages.ERROR_QUEUE_POINTER_STACK_OVERFLOW.getId();
			
			log.info(id + ": The pointer is alread in stack overflow.");

			return null;
		}

		return new ArrayList<String>(this.collection).get(localPointer);
	}

	/**
	 * @see org.adaptools.base.BasicQueue#readPrevious()
	 */
	public String readPrevious() {
		Integer localPointer = this.pointer - 1;

		// log case the pointer in stack overflow and return null
		if (localPointer >= this.collection.size()) {
			String id = AdaptoolsBaseMessages.ERROR_QUEUE_POINTER_STACK_OVERFLOW.getId();
			
			log.info(id + ": The pointer is alread in stack overflow.");
			
			return null;
		}

		return new ArrayList<String>(this.collection).get(localPointer);
	}

	/**
	 * @see org.adaptools.base.BasicQueue#readNext(Integer)
	 */
	public String readNext(Integer value) {
		if (value == null || value < 1) {
			return this.read();
		}

		Integer localPointer = this.pointer + value;

		// log case the pointer in stack overflow and return null
		if (localPointer >= this.collection.size()) {
			String id = AdaptoolsBaseMessages.ERROR_QUEUE_POINTER_STACK_OVERFLOW.getId();
			
			log.info(id + ": The pointer is alread in stack overflow.");
			
			return null;
		}

		return new ArrayList<String>(this.collection).get(localPointer);
	}
	
	
	/**
	 * @see org.adaptools.base.BasicQueue#hasNext()
	 */
	@Override
	public boolean hasNext() {
		
		String value = this.readNext();
		
		if (value != null){
			return true;
		}
		
		return false;
	}

	/**
	 * @see org.adaptools.base.BasicQueue#readPrevious(Integer)
	 */
	public String readPrevious(Integer value) {
		if (value == null || value < 1) {
			return this.read();
		}

		Integer localPointer = this.pointer - value;

		// log case the pointer in stack overflow and return null
		if (localPointer >= this.collection.size()) {
			String id = AdaptoolsBaseMessages.ERROR_QUEUE_POINTER_STACK_OVERFLOW.getId();
			
			log.info(id + ": The pointer is alread in stack overflow.");
			
			return null;
		}

		return new ArrayList<String>(this.collection).get(localPointer);
	}

	/**
	 * @see org.adaptools.base.BasicQueue#add(String)
	 */
	public void add(String value) {
		this.collection.add(value);
	}

	/**
	 * @see org.adaptools.base.BasicQueue#addAll(Collection)
	 */
	@Override
	public void addAll(Collection<String> stringCollection) {
		for (String string : stringCollection) {
			this.add(string);
		}
	}

	/**
	 * @return the pointer
	 */
	public Integer getPointer() {
		return pointer;
	}

	/**
	 * @param pointer
	 *            the pointer to set
	 */
	public void setPointer(Integer pointer) {
		this.pointer = pointer;
	}

	/**
	 * @return the Collection
	 */
	public Collection<String> getCollection() {
		return collection;
	}

	/**
	 * @param Collection
	 *            the Collection to set
	 */
	public void setCollection(Collection<String> Collection) {
		this.collection = Collection;
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((collection == null) ? 0 : collection.hashCode());
		result = prime * result + ((pointer == null) ? 0 : pointer.hashCode());
		return result;
	}

	/**
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof BasicQueue)) {
			return false;
		}
		BasicQueue other = (BasicQueue) obj;
		if (collection == null) {
			if (other.collection != null) {
				return false;
			}
		} else if (!collection.equals(other.collection)) {
			return false;
		}
		if (pointer == null) {
			if (other.pointer != null) {
				return false;
			}
		} else if (!pointer.equals(other.pointer)) {
			return false;
		}
		return true;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();

		for (String s : this.collection) {
			builder.append(s);
		}

		return builder.toString();
	}

	@Override
	public Queue copy() {
		Collection<String> collection = new ArrayList<String>();
		Queue queue = new BasicQueue();
		
		for (String string : this.collection) {
			collection.add(new String(string));
		}
		
		queue.setCollection(collection);
		queue.setPointer(this.pointer);
		
		return queue;
	}
	
}